package com.aihuishou.monitor.rpc;

import com.aihuishou.monitor.LogConstants;
import com.aihuishou.monitor.LogMonitor;
import com.aihuishou.monitor.MessageThreadLocal;
import com.aihuishou.monitor.model.DefaultTransaction;
import com.alibaba.fastjson2.JSON;
import feign.InvocationHandlerFactory;
import feign.Target;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FeignClient;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

@Slf4j
public class CustomInvocationHandlerFactory implements InvocationHandlerFactory {

    private final InvocationHandlerFactory delegateFactory;

    public CustomInvocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {
        this.delegateFactory = invocationHandlerFactory;
    }

    @Override
    public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
        // 获取默认的 InvocationHandler
        InvocationHandler handler = delegateFactory.create(target, dispatch);

        // 返回一个新的 InvocationHandler，包装默认的 handler
        return (proxy, method, args) -> {
            // 如果方法来自 Object 类，直接调用
            if (method.getDeclaringClass() == Object.class) {
                return handler.invoke(proxy, method, args);
            }

            Class<?> clazz = target.type();

            DefaultTransaction t = LogMonitor.newTransaction(
                LogConstants.TYPE_RPC_CALL, clazz.getSimpleName() + "." + method.getName()
            );

            this.logProviderEvent(args, clazz);

            try {
                t.setStatus(LogConstants.SUCCESS);
                // 执行原始的 handler 方法
                Object data = handler.invoke(proxy, method, args);
                this.logResultEvent(data);
                return data;
            } catch (Throwable throwable) {
                t.setStatus(throwable.getClass().getName());
                throw throwable;
            } finally {
                try {
                    t.complete();
                    MessageThreadLocal.getContext().endMessage(t);
                } catch (Exception ignored) {
                }
            }
        };
    }

    private void logProviderEvent(Object[] args, Class<?> clazz) {
        try {
            FeignClient annotation = clazz.getAnnotation(FeignClient.class);
            LogMonitor.logEvent(LogConstants.TYPE_RPC_CALL, LogConstants.TYPE_RPC_CALL_PROVIDER,
                annotation != null && annotation.url() != null && !"".equals(annotation.url())
                    ? annotation.url() : "unknown"
            );
            String str = "unknown";
            if (args != null) {
                str = JSON.toJSONString(args);
                str = str.length() > 100 ? str.substring(0, 100) : str;
            }
            LogMonitor.logEvent(LogConstants.TYPE_RPC_CALL, LogConstants.TYPE_RPC_CALL_PARAM, str);
        } catch (Exception ignored) {
        }
    }

    private void logResultEvent(Object data) {
        try {
            String str = "unknown";
            if (data != null) {
                str = JSON.toJSONString(data);
                str = str.length() > 100 ? str.substring(0, 100) : str;
            }
            LogMonitor.logEvent(LogConstants.TYPE_RPC_CALL, LogConstants.TYPE_RPC_CALL_RESULT, str);
        } catch (Exception ignored) {
        }
    }
}